import { createAsyncThunk, createSlice, isRejected } from "@reduxjs/toolkit";
import { GetParams, PageInfo, Warehouse, WarehouseEntry, int } from "../types";
import { RootState } from "./store";
import { deleteRequest, getRequest, postRequest, putRequest } from "../util/Net";

// 1. Requests.

type GetWarehouseRequest = GetParams & {
  name?: string;
};

type CreateWarehouseRequest = Warehouse;

type UpdateWarehouseRequest = Warehouse;

interface DeleteWarehouseRequest {
  id: int;
}

// 2. Responses.

type GetWarehouseResponseEntry = WarehouseEntry;

type GetWarehouseResponse = PageInfo<GetWarehouseResponseEntry>;

// 3. Params for reducers.

type GetWarehouseParams = GetWarehouseRequest;

type CreateWarehouseParams = Warehouse;

type UpdateWarehouseParams = WarehouseEntry;

type DeleteWarehouseParams = DeleteWarehouseRequest;

// 4. State and initial value definition.

interface WarehouseManagementState {
  loaded: boolean;
  entries: WarehouseEntry[];
  typesLoaded: boolean;
  types: string[];
}

const initialState: WarehouseManagementState = {
  loaded: false,
  entries: [],
  typesLoaded: false,
  types: []
};

// 5. Async functions definition.

const getWarehouses = createAsyncThunk('/warehouse/get', async (params: GetWarehouseParams) => {
  const response = await getRequest<GetWarehouseResponse, GetWarehouseRequest>({
    url: '/warehouse',
    params: params
  });
  return response.data;
});

const createWarehouse = createAsyncThunk('/warehouse/post', async (params: CreateWarehouseParams) => {
  const response = await postRequest<null, CreateWarehouseRequest>({
    url: '/warehouse',
    request: params
  });
  return response.data;
});

const updateWarehouse = createAsyncThunk('/warehouse/put', async (params: UpdateWarehouseParams) => {
  const response = await putRequest<null, UpdateWarehouseRequest>({
    url: `/warehouse/${params.id}`,
    request: {
      name: params.name,
      type: params.type,
      address: params.address
    }
  });
  return response.data;
});

const deleteWarehouse = createAsyncThunk('/warehouse/delete', async (params: DeleteWarehouseParams) => {
  const response = await deleteRequest<null, null, DeleteWarehouseRequest>({
    url: `/warehouse/${params.id}`,
    params: params
  });
  return response.data;
});

const getWarehouseTypes = createAsyncThunk('/warehouse/types/get', async () => {
  const response = await getRequest<string[]>({
    url: '/warehouse/type'
  });
  return response.data;
});

// 6. Slice definition.

const warehouseManagementSlice = createSlice({
  name: 'warehouseManagement',
  initialState: initialState,
  reducers: {
    clear: (state) => {
      state.entries = [];
      state.loaded = false;
      state.types = [];
      state.typesLoaded = false;
    },
    refresh: (state) => {
      state.loaded = false;
    }
  },
  extraReducers: (builder) => {
    builder.addCase(getWarehouses.fulfilled, (state, action) => {
      state.entries = action.payload.result.data;
      state.loaded = true;
    }).addCase(createWarehouse.fulfilled, (state) => {
      state.loaded = false;
    }).addCase(updateWarehouse.fulfilled, (state) => {
      state.loaded = false;
    }).addCase(deleteWarehouse.fulfilled, (state) => {
      state.loaded = false;
    }).addCase(getWarehouseTypes.fulfilled, (state, action) => {
      state.types = action.payload.result;
      state.typesLoaded = true;
    }).addMatcher(isRejected, (state, action) => {
      console.error(action.error);
    });
  }
});

export default warehouseManagementSlice.reducer;
export const warehouseManagementActions = { ...warehouseManagementSlice.actions, getWarehouses, createWarehouse, updateWarehouse, deleteWarehouse, getWarehouseTypes };
export const selectWarehouseEntries = (root: RootState) => root.warehouseManagementReducer.entries;
export const selectWarehouseEntriesLoaded = (root: RootState) => root.warehouseManagementReducer.loaded;
export const selectWarehouseTypes = (root: RootState) => root.warehouseManagementReducer.types;
export const selectWarehouseTypesLoaded = (root: RootState) => root.warehouseManagementReducer.typesLoaded;